/*
 * Inode.cpp
 *
 *  Created on: Apr 19, 2011
 *      Author: 
 */

#include "Inode.h"
#include <string.h>
#include "l3tol.h"
#include "e7convert.h"
#include "FS.h"

const int xyz = 1;
#define isBigEndian() ((*(char*)&xyz) == 0)

Inode::Inode(InodeBlock* iBlock, ino_x iNumber){
	myBlock = iBlock;
	inr = iNumber;
	inDir1 = NULL;
	inDir2 = NULL;
	inDir3 = NULL;
	parseInodeBlock();
	activity = 0;
	//print();
}

void Inode::reset(){
	cout << "punt i0" << endl;
	setMode(0);
	cout << "punt i1" << endl;
	setLinks(1);
	cout << "punt i2" << endl;
	FS* fs = FS::getInstance();
	cout << "punt i3" << endl;
	setOwner(fs->getUserID());
	cout << "punt i4" << endl;
	setGroup(fs->getGroupID());
	cout << "punt i5" << endl;
	setSize(0);
	cout << "punt i6" << endl;
	for(int i = 0; i < 13; i++){
		addr[i] = 0;
	}
	cout << "punt i7" << endl;
	setAddr(0,0);
	cout << "punt i8" << endl;
	setLastAccessTime();
	cout << "punt i9" << endl;
	setLastModifiedTime();
	cout << "punt i10" << endl;
	setCreatedTime();
	cout << "punt i11" << endl;
}

Inode::~Inode() {
	myBlock = 0; //maybe delete?
	inr = 0;
	mode = 0;
	links = 0;
	uid = 0;
	gid = 0;
	size = 0;
	for(int i = 0; i < 13; i++){
		addr[i] = 0;
	}
	delete addr;
	aTime = 0;
	mTime = 0;
	cTime = 0;
}

void Inode::setMode(int newMode){
	cout << "punt i21" << endl;
	mode = newMode;
	cout << "punt i22" << endl;
	byte data[2];
	cout << "punt i23" << endl;
	byte* nr = (byte*) &mode;
	cout << "punt i24" << endl;

	cout << (int)nr[0] << endl;
	cout << (int)nr[1] << endl;
	cout << (int)nr[2] << endl;
	cout << (int)nr[3] << endl;

	if(isBigEndian()){
		data[0] = nr[2];
		data[1] = nr[3];
	}else{
		data[0] = nr[1];
		data[1] = nr[0];
	}
	cout << "punt i25" << endl;
	int loc = (itoo(inr) * INSIZ);
	cout << "punt i26" << endl;
	FS::getInstance()->writeInBlock(myBlock->getNumber(), loc, data, 2);
	cout << "punt i27" << endl;
}

void Inode::increaseLinkCount(){
	links++;
	writeLinks();
}

void Inode::decreaseLinkCount(){
	links--;
	writeLinks();
}

void Inode::writeLinks(){
	byte data[2];
	byte* nr = (byte*) &links;
	if(isBigEndian()){
		data[0] = nr[2];
		data[1] = nr[3];
	}else{
		data[0] = nr[1];
		data[1] = nr[0];
	}
	int loc = (itoo(inr) * INSIZ)+2;
	FS::getInstance()->writeInBlock(myBlock->getNumber(), loc, data, 2);
}

void Inode::setOwner(int newOwner){
	uid = newOwner;
	byte data[2];
	byte* nr = (byte*) &uid;
	if(isBigEndian()){
		data[0] = nr[2];
		data[1] = nr[3];
	}else{
		data[0] = nr[1];
		data[1] = nr[0];
	}
	int loc = (itoo(inr) * INSIZ)+4;
	FS::getInstance()->writeInBlock(myBlock->getNumber(), loc, data, 2);
}

void Inode::setGroup(int newGroup){
	gid = newGroup;
	byte data[2];
	byte* nr = (byte*) &gid;
	if(isBigEndian()){
		data[0] = nr[2];
		data[1] = nr[3];
	}else{
		data[0] = nr[1];
		data[1] = nr[0];
	}
	int loc = (itoo(inr) * INSIZ)+6;
	FS::getInstance()->writeInBlock(myBlock->getNumber(), loc, data, 2);
}

void Inode::setSize(long newSize){
	byte list[4];
	byte* nr = (byte*) &newSize;
	if(isBigEndian()){
		list[0] = nr[0];
		list[1] = nr[1];
		list[2] = nr[2];
		list[3] = nr[3];
	}else{
		list[0] = nr[3];
		list[1] = nr[2];
		list[2] = nr[1];
		list[3] = nr[0];
	}
	int loc = (itoo(inr) * INSIZ)+8;
	FS::getInstance()->writeInBlock(myBlock->getNumber(), loc, list, 4);
}

void Inode::setAddr(int location, long newAddr){
	if(location < 10){ //the location is inside the inode itself
		addr[location] = newAddr;
		//onderstaande van if block vervangen met write()
		byte data[39];
		for(int i = 0; i < 13; i++){
			byte* nr = (byte*) &addr[i];
			if(isBigEndian()){
				data[i*3+0] = nr[1];
				data[i*3+1] = nr[2];
				data[i*3+2] = nr[3];
			}else{
				data[i*3+0] = nr[2];
				data[i*3+1] = nr[1];
				data[i*3+2] = nr[0];
			}
		}
		int loc = (itoo(inr) * INSIZ)+12;
		FS::getInstance()->writeInBlock(myBlock->getNumber(), loc, data, 39);
	}else if(location < 138){
		if(addr[10] == 0){//what if indirblock doesnt exist yet
			addr[10] = FS::getInstance()->getSuperBlock()->getFreeBlockAddress();
			byte data[DSKBLKSIZ];
			for(int i = 0; i < DSKBLKSIZ; i++){
				data[i] = 0;
			}
			inDir1 = new InDir1Block(addr[10], data);
		}
		inDir1->addAddress(newAddr, location-10);
	}else if(location < 16522){
		if(addr[11] == 0){//what if indirblock doesnt exist yet
			addr[11] = FS::getInstance()->getSuperBlock()->getFreeBlockAddress();
			byte data[DSKBLKSIZ];
			for(int i = 0; i < DSKBLKSIZ; i++){
				data[i] = 0;
			}
			inDir2 = new InDir2Block(addr[11], data);
		}
		inDir2->addAddress(newAddr, location-138);
	}else if(location < 2113674){
		if(addr[12] == 0){//what if indirblock doesnt exist yet
			addr[12] = FS::getInstance()->getSuperBlock()->getFreeBlockAddress();
			byte data[DSKBLKSIZ];
			for(int i = 0; i < DSKBLKSIZ; i++){
				data[i] = 0;
			}
			inDir3 = new InDir3Block(addr[12], data);
		}
		inDir3->addAddress(newAddr, location-16522);
	}
}

void Inode::setLastAccessTime(){
	aTime = time(NULL);
	writeTime(1);
}

void Inode::setLastModifiedTime(){
	mTime = time(NULL);
	writeTime(2);
}

void Inode::setCreatedTime(){
	cTime = time(NULL);
	writeTime(3);
}

void Inode::writeTime(int type){
	long t;
	int offset = 0;;
	if(type == 1){
		t = aTime;
	}else if(type == 2){
		t = mTime;
		offset = 4;
	}else if(type == 3){
		t = cTime;
		offset = 8;
	}else{
		cout << "unvalid time type" << endl;
		return;
	}
	byte list[4];
	byte* nr = (byte*) &t;
	if(isBigEndian()){
		list[0] = nr[0];
		list[1] = nr[1];
		list[2] = nr[2];
		list[3] = nr[3];
	}else{
		list[0] = nr[3];
		list[1] = nr[2];
		list[2] = nr[1];
		list[3] = nr[0];
	}
	int loc = (itoo(inr) * INSIZ)+52+offset;
	FS::getInstance()->writeInBlock(myBlock->getNumber(), loc, list, 4);
}

void Inode::parseInodeBlock(){
	// read data from myBlock, parse them into the right type and set attribute with them
	int offset = itoo(inr) * INSIZ;
	mode = myBlock->readUShort(offset); //unsigned short
	offset+=2;
	links = myBlock->readShort(offset); //(normal) short
	offset+=2;
	uid = myBlock->readUShort(offset); //unsigned short
	offset+=2;
	gid = myBlock->readUShort(offset); //unsigned short
	offset+=2;
	size = myBlock->readLong(offset);
	offset+=4;
	byte addresses[39]; //13 3-byte longs
	for(int i = 0; i < 39; i++){
		addresses[i] = myBlock->readByte(offset);
		offset++;
	}
	offset++; //skipping last byte
	l3tol(addr, addresses, 13); //convert 3-byte longs to 4-byte longs
	if(addr[10] != 0){
		inDir1 = new InDir1Block(addr[10], FS::getInstance()->readBlock(addr[10]));
	}
	if(addr[11] != 0){
		inDir2 = new InDir2Block(addr[11], FS::getInstance()->readBlock(addr[11]));
	}
	if(addr[12] != 0){
		inDir3 = new InDir3Block(addr[12], FS::getInstance()->readBlock(addr[12]));
	}
	aTime = myBlock->readLong(offset);
	offset+=4;
	mTime = myBlock->readLong(offset);
	offset+=4;
	cTime = myBlock->readLong(offset);
	offset+=4;
}

list<long> * Inode::getFileData(){
	list<long> * alist = new list<long>();
	for(int i = 0; i < 13; i++){
		if(addr[i] > 0){
			alist->push_back(addr[i]);
		}else break;
	}
	return alist;
}

string Inode::modeString(){
	int mod = mode;
	string s = "";
	if(mod >= S_IFMT){
		s.append("?");
		//mod -= S_IFMT;
	}else if(mod >= S_IFREG){
		s.append("-");
		mod -= S_IFREG;
	}else if(mod >= S_IFMPB){
		s.append("s"); //or l?
		mod -= S_IFMPB;
	}else if(mod >= S_IFBLK){
		s.append("b");
		mod -= S_IFBLK;
	}else if(mod >= S_IFDIR){
		s.append("d");
		mod -= S_IFDIR;
	}else if(mod >= S_IFMPC){
		s.append("l"); //or s?
		mod -= S_IFMPC;
	}else if(mod >= S_IFCHR){
		s.append("c");
		mod -= S_IFCHR;
	}else if(mod >= S_IFIFO){
		s.append("p");
		mod -= S_IFIFO;
	}else{
		s.append("0");
	}
	if(mod >= S_ISUID)
		mod -= S_ISUID;
	if(mod >= S_ISGID)
		mod -= S_ISGID;
	if(mod >= S_ISVTX)
		mod -= S_ISVTX;
	if(mod >= S_IUREAD){
		s.append("r");
		mod -= S_IUREAD;
	}else s.append("-");
	if(mod >= S_IUWRITE){
		s.append("w");
		mod -= S_IUWRITE;
	}else s.append("-");
	if(mod >= S_IUEXEC){
		s.append("x");
		mod -= S_IUEXEC;
	}else s.append("-");
	if(mod >= S_IGREAD){
		s.append("r");
		mod -= S_IGREAD;
	}else s.append("-");
	if(mod >= S_IGWRITE){
		s.append("w");
		mod -= S_IGWRITE;
	}else s.append("-");
	if(mod >= S_IGEXEC){
		s.append("x");
		mod -= S_IGEXEC;
	}else s.append("-");
	if(mod >= S_IOREAD){
		s.append("r");
		mod -= S_IOREAD;
	}else s.append("-");
	if(mod >= S_IOWRITE){
		s.append("w");
		mod -= S_IOWRITE;
	}else s.append("-");
	if(mod >= S_IOEXEC){
		s.append("x");
		mod -= S_IOEXEC;
	}else s.append("-");
	return s;
}

void Inode::print(){
	cout << "...PRINTING INODE..." << endl;
	cout << "number:	" << inr << endl;
	cout << "mode raw:  " << mode << endl;
	cout << "mode:		" << modeString() << endl;
	cout << "links:		" << links << endl;
	cout << "user:		" << uid << endl;
	cout << "group:		" << gid << endl;
	cout << "size:		" << size << endl;
	cout << "last access: 		" << ctime(&aTime) << endl;
	cout << "last modification:	" << ctime(&mTime) << endl;
	cout << "time created:		" << ctime(&cTime) << endl;
	cout << "DATABLOCKS" << endl;
	for(int i = 0; i < 13; i++){
		cout << " " << i << ":		" << addr[i] << endl;
	}
	if(inDir1 != NULL){
		cout << "INDIR1BLOCK: " << addr[10] << endl;
		inDir1->print();
		cout << endl;
	}
	if(inDir1 != NULL){
		cout << "INDIR2BLOCK: " << addr[11] << endl;
		inDir2->print();
		cout << endl;
	}
	if(inDir1 != NULL){
		cout << "INDIR3BLOCK: " << addr[12] << endl;
		inDir3->print();
		cout << endl;
	}
}

int Inode::getDiskBlockNr(int nr){
	if(nr < 10){
		return addr[nr];
	}else if(nr < 138){
		return inDir1->getAddress(nr-10);
	}else if(nr < 16522){
		return inDir2->getAddress(nr-138);
	}else if(nr < 2113674){
		return inDir3->getAddress(nr-16522);
	}else{
		return 0;
	}
}

void Inode::free(){
	//print();
	cout << "free: 1" << endl;
	FS* fs = FS::getInstance();
	for(int i = 0; i < 10 && i < size/DSKBLKSIZ+1; i++){
		if(addr[i] != 0){
			cout << "free block " << i << ", address: " << addr[i] << endl;
			fs->addFreeBlock(addr[i]);
			cout << "free: 2" << endl;
		}
	}
	if(addr[10] != 0){
		cout << "free: 3" << endl;
		inDir1->free();
		fs->addFreeBlock(addr[10]);
	}
	if(addr[11] != 0){
		cout << "free: 4" << endl;
		inDir2->free();
		fs->addFreeBlock(addr[11]);
	}
	if(addr[12] != 0){
		cout << "free: 5" << endl;
		inDir3->free();
		fs->addFreeBlock(addr[11]);
	}
}
